﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.Collections;
using System.IO;
using System.Diagnostics;
using log4net;
using RollBack.src.ui;
using RollbackLib;

namespace RollBack.src.ui
{
    /// <summary>
    /// The tree view of left side of the explorer window
    /// </summary>
    public partial class ExplorerLeftPanel : UserControl
    {
        private RollBackLib.src.sys.Sysutils sysutils;
        private static readonly ILog log = LogManager.GetLogger(typeof(ExplorerLeftPanel));
        public ExplorerView.ExplorerData m_explorerViewData
        {
            get;
            set;
        }

        public ExplorerLeftPanel()
        {
            sysutils = new RollBackLib.src.sys.Sysutils();
            InitializeComponent();
            this.m_leftTreeView.ImageList = ImageResources.m_resources.m_imageList;
            RefreshRootNode(1);
        }

        public void onMouseClick(object sender, TreeNodeMouseClickEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                TreeViewHitTestInfo info = m_leftTreeView.HitTest(e.Location);
                if (info.Node != null && info.Location == TreeViewHitTestLocations.StateImage)
                {
                    if (m_leftTreeView is ThreeStateTreeView)
                    {
                        ThreeStateTreeView tv = (ThreeStateTreeView)m_leftTreeView;
                        tv.checkNodesParentAndChildren(info.Node);
                    }                    
                }
            }
        }

        public TreeNode getCDriveNode(string name)
        {
            TreeNode[] tn = m_leftTreeView.Nodes.Find(name,true);

            if (tn.Length > 0)
            {
                return tn[0];
            }
            return null;
        }

        /// <summary>
        /// Expand the tree node by reading the contents of the directory represented by the treenode.
        /// </summary>
        /// <param name="curr"></param>
        public void expandNode(TreeNode curr,bool force)
        {
            m_leftTreeView.SelectedNode = curr;
            if (force)
            {
                curr.Nodes.Clear();
            }
            updateParentAndChildren(curr);
            if (m_explorerViewData != null)
            {
                if (m_explorerViewData.RootDisplayNode != null)
                {
                    bool isRootNode = curr.FullPath.Equals(m_explorerViewData.RootDisplayNode, StringComparison.InvariantCultureIgnoreCase);
                    m_explorerViewData.m_leftData.expandNode(curr, isRootNode, m_explorerViewData);
                }
            }
            curr.ImageIndex = ImageResources.Indexes.m_folderIconClosed;
        }

        /// <summary>
        /// Refresh the root node and display it on the left panel.
        /// </summary>
        /// <param name="levlesToExpand">How many levels of the top root node to expand</param>
        /// <returns>The Last treenode that was expanded. Can be null</returns>
        public TreeNode RefreshRootNode(int levelsToExpand)
        {
            TreeNode node = null;
            if (m_explorerViewData != null)
            {
                m_leftTreeView.Nodes.Clear();
                TreeNode tn = m_leftTreeView.Nodes.Add(m_explorerViewData.RootDisplayNode,m_explorerViewData.RootDisplayNode);
                tn.ImageIndex = ImageResources.Indexes.m_folderIconClosed;
                //tn.StateImageIndex = ThreeStateTreeView.STATE_UNCHECKED;
                m_explorerViewData.m_leftData.expandNode(tn, true, m_explorerViewData);
                updateParentAndChildren(tn);
                if (levelsToExpand > 0)
                {
                    node = getCDriveNode(m_explorerViewData.RootDisplayNode);
                    while (--levelsToExpand > 0 && node !=null)
                    {
                        node = node.FirstNode;
                        if (node != null)
                        {
                            expandNode(node, false /*force */);
                            node.Expand();
                            m_leftTreeView.Select();
                            m_leftTreeView.SelectedNode = node;
                            m_leftTreeView.SelectedNode.ImageIndex = ImageResources.Indexes.m_folderIconClosed;
                        }
                    }
                }
            }
            //this.treeView1.HotTracking = true;
            this.m_leftTreeView.ItemHeight = 20;
            this.m_leftTreeView.ShowNodeToolTips = true;
            return node;
        }

        private void updateParentAndChildren(TreeNode tn)
        {
            tn.ImageIndex = ImageResources.Indexes.m_folderIconClosed;
            if (m_leftTreeView is ThreeStateTreeView)
            {
                ThreeStateTreeView tv = (ThreeStateTreeView)m_leftTreeView;
                tv.UpdateChildren(tn);
                tv.UpdateParent(tn);
            }
        }

        public void AddTreeViewDelegate(System.Windows.Forms.TreeViewEventHandler target) {
            this.m_leftTreeView.AfterSelect += new System.Windows.Forms.TreeViewEventHandler(target);
        }

        /// <summary>
        /// return the selected node
        /// </summary>
        /// <returns></returns>
        public TreeNode getSelectedNode()
        {
            return m_leftTreeView.SelectedNode;
        }
        #region DragAndDropFunctions
        private void listView1_ItemDrag(object sender, ItemDragEventArgs e)
        {
            if (m_explorerViewData.m_leftData.m_allowDrag)
            {
                log.Debug("Dragging " + e.Item);
                StringBuilder sb = new StringBuilder();
                TreeNode selected = null;

                if (e.Item is TreeNode)
                {
                    selected = (TreeNode)e.Item;
                    m_leftTreeView.SelectedNode = selected;
                }
                else
                {
                    selected = this.m_leftTreeView.SelectedNode;
                }
                // convert the selected items in the file list view to a comma separated list
                if (selected != null)
                {
                    if (selected.FullPath.Length > m_explorerViewData.RootDisplayNodePath.Length)
                    {
                        string path = m_explorerViewData.m_rightData.getPathOnDisk(m_explorerViewData) + removeRootPath(selected.FullPath);
                        sb.Append(", " + path);
                        sb.Insert(0, "" + this.m_leftTreeView.GetHashCode());
                        string hashCode = sb.ToString();
                        base.DoDragDrop(hashCode, DragDropEffects.Copy);
                    }
                }
                else
                {
                    log.Debug("Drag and Drop has no selection.");
                }
            }
        }

        /// <summary>
        /// Remove the root nodes path from the full path
        /// </summary>
        /// <param name="p"></param>
        /// <returns></returns>
        private string removeRootPath(string fullpath)
        {
            string s = fullpath.Substring(m_explorerViewData.RootDisplayNodePath.Length);
            return s;
        }

        private void listView1_DragEnter(object sender, DragEventArgs e)
        {
            if (m_explorerViewData.m_leftData.m_allowDrop)
            {
                base.OnDragEnter(e);
                string[] list = GetDragData(sender, e);
                if (list != null)
                {
                    String text = (String)e.Data.GetData(DataFormats.Text);
                    log.Info("Getting " + text + " from drag enter");
                    e.Effect = DragDropEffects.Copy;
                }
            }
        }

        private string[] GetDragData(object sender, DragEventArgs e)
        {
            string[] list = ParseDragData(e);
            if (list.Length <= 1)
            {
                log.Info("Data length too short");
                return null;
            }
            bool me = IsMyListView(list[0]);
            if (!e.Data.GetDataPresent(DataFormats.Text) || me)
            {
                if (!me) {
                    log.Info("Drag data not in text format");
                }
                e.Effect = DragDropEffects.None;
                return null;
            }
            log.Info("this=" + this.m_leftTreeView.GetHashCode() + " sender=" + sender.GetHashCode());
            return list;
        }

        private string[] ParseDragData(DragEventArgs e)
        {
            string[] list = null;
            if (!e.Data.GetDataPresent(DataFormats.Text))
            {
                // data not available or not in string format.
                log.Info("No data present");
                return null;
            }
            string data = (string)e.Data.GetData(DataFormats.Text);
            list = data.Split(',');
            return list;
        }

        // return true if the sender of the drag event happens to be myself.
        private bool IsMyListView(string me)
        {
            int hashCode = Int32.Parse(me);
            if (hashCode == this.m_leftTreeView.GetHashCode())
            {
                return true;
            }
            return false;
        }

        private void listView1_DragLeave(object sender, EventArgs e)
        {
            base.OnDragLeave(e);
        }

        private void listView1_DragDrop(object sender, DragEventArgs e) {
            string[] list = GetDragData(sender, e);
            if (list != null) {
                String text = (String)e.Data.GetData(DataFormats.Text);
                log.Info("Getting " + text + " from drag enter");
                e.Effect = DragDropEffects.Copy;
                for (int i = 1; i < list.Length; i++ ) {
                    string d = list[i];
                    //this.Add(d,true);
                }
            }
        }
        #endregion
    }
}
